#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
//第10讲：操作符详解
//3. 原码、反码、补码
//整数的2进制表⽰⽅法有三种，即原码、反码和补码

// 有符号整数的三种表⽰⽅法均有符号位和数值位两部分，2进制序列中，最⾼位的1位是被当做符号
//位，剩余的都是数值位。

//负整数的三种表⽰⽅法各不相同。

//int main()
//{
	//正整数的原、反、补码都相同。
	//int a = 10;//a是四个字节，32bit
	//00000000 00000000 00000000 00001010 - 原码
	//00000000 00000000 00000000 00001010 - 反码
	//00000000 00000000 00000000 00001010 - 补码

	//int b = -10;
	//10000000 00000000 00000000 00001010 - 原码：直接将数值按照正负数的形式翻译成⼆进制得到的就是原码。
	//11111111 11111111 11111111 11110101 - 反码：将原码的符号位不变，其他位依次按位取反就可以得到反码。
	//11111111 11111111 11111111 11110110 - 补码：反码+1就得到补码。

	//补码得到原码也是可以使⽤：取反，+1的操作。

	//对于整形来说：数据存放内存中其实存放的是补码。
	//为什么呢？
	//	在计算机系统中，数值⼀律⽤补码来表⽰和存储。原因在于，使⽤补码，可以将符号位和数值域统⼀
	//	处理；同时，加法和减法也可以统⼀处理（CPU只有加法器）此外，补码与原码相互转换，其运算
	//	过程是相同的，不需要额外的硬件电路。
	//例子:计算:1-1
	//假设用原码进行计算:
	// 1的原码:00000000 00000000 00000000 00000001
	//-1的原码:10000000 00000000 00000000 00000001
	//如果最高位（符号位）不进行计算，得到 10000000 00000000 00000000 00000010 结果为: -2
	//如果最高位（符号位）  进行计算，得到 00000000 00000000 00000000 00000010 结果为: 2
	// 两种方法显然都不对

	//假设用补码进行计算:
	// 1的补码:00000000 00000000 00000000 00000001
	//-1的补码:11111111 11111111 11111111 11111111
	//得到: 1 00000000 00000000 00000000 00000000 最高第33位舍弃，结果为0



	//4. 移位操作符
	//	<< 左移操作符
	//	>> 右移操作符
	//	注：移位操作符的操作数只能是整数。

	//4.1 左移操作符
	//	移位规则：左边抛弃、右边补0
	//int num = 10;//补码: 00000000 00000000 00000000 00001010
	//int n = num << 1;//左移一位后: 00000000 00000000 00000000 00010100 结果为20（有乘2的效果)
	//printf("n= %d\n", n);
	//printf("num= %d\n", num);

	//num = -10;//补码: 11111111 11111111 11111111 11110110
	//n = num << 1;//左移一位后: 11111111 11111111 11111111 11101100(补码，还需要转换为原码)
	//printf("n= %d\n", n);// 原码: 10000000 00000000 00000000 00010100 结果为:-20
	//printf("num= %d\n", num);

	//4.2 右移操作符
	//	移位规则：⾸先右移运算分两种：
	//	1. 逻辑右移：左边⽤0填充，右边丢弃
	//	2. 算术右移：左边⽤原该值的符号位填充，右边丢弃
	//右移到底是算数右移还是逻辑右移，取决于编译器的实现，常见的编译器都是算数右移

	//复合赋值: a>>=1 a的补码右移1位后赋给a
	//		   a<<=1 a的补码左移1位后赋给a

	//警告⚠️：对于移位运算符，不要移动负数位，这个是标准未定义的。



	//5. 位操作符： & 、 | 、 ^ 、~
	//位操作符有：
	//	& 按位与
	//	| 按位或
	//	^ 按位异或
	//	~ 按位取反
	//注：他们的操作数必须是整数。
	
	//&按（二进制）位与
	//int a = 3;// 00000000 00000000 00000000 00000011 3补码
	//int b = -5;// 00000000 00000000 00000000 00000101 -5原码
			   // 11111111 11111111 11111111 11111010 -5反码
			   // 11111111 11111111 11111111 11111011 -5补码
	//int c = a & b;
	//用补码进行计算:(两个为1结果才为1)
	// 00000000 00000000 00000000 00000011 3补码
	// 11111111 11111111 11111111 11111011 -5补码
	// 00000000 00000000 00000000 00000011 结果(补码，但最高位为0是正数，原码和补码相同)
	//printf("c = %d\n", c);

	// |按（二进制）位或(只要有1就为1)
	//int d = a | b;
	// 00000000 00000000 00000000 00000011 3补码
	// 11111111 11111111 11111111 11111011 -5补码
	// 11111111 11111111 11111111 11111011 结果(补码 最高位为1是负数，需要得到原码)
	// 10000000 00000000 00000000 00000101 结果为-5(原码)
	//printf("d = %d\n", d);

	//	^ 按（二进制）位异或(相同为0，相异为1)
	// 00000000 00000000 00000000 00000011 3补码
	// 11111111 11111111 11111111 11111011 -5补码
	// 11111111 11111111 11111111 11111000 结果（补码)
	// 10000000 00000000 00000000 00001000 结果（原码）
	//int e = a ^ b;
	//printf("e = %d\n", e);
	
	// ~按位取反
	// 00000000 00000000 00000000 00000011 3补码
	// 11111111 11111111 11111111 11111100 取反（补码）
	// 10000000 00000000 00000000 00000100 原码(-4)
	//int f = ~a;
	//printf("f = %d", f);
//	return 0;
//}

//int main()
//{
	//⼀道变态的⾯试题：
	//不能创建临时变量（第三个变量），实现两个整数的交换。
	//int a = 3;
	//int b = 5;
	//a = a ^ b;
	//b = a ^ b;//相当于:b=a^b^b=a^0=a
	//a = a ^ b;//相当于:a=a^b^a=b^0=b
	//printf("交换后:\na=%d\nb=%d\n", a, b);
//	return 0;
//}
//练习1：编写代码实现：求⼀个整数存储在内存中的⼆进制中1的个数。
//解法1:
//int main()
//{
//	unsigned int a = 0;
//	scanf("%d", &a);
//	int count = 0;
//	while (a)
//	{
//		if (a % 2 == 1)
//		{
//			count++;
//		}
//		a /= 2;
//	}
//	printf("%d", count);
//	return 0;
//}

//解法2:
//int main()
//{
//	int a = 0;
//	scanf("%d", &a);
//	int count = 0;
//	for (int i = 0; i < 32; i++)
//	{
//		if ((a >> i) & 1 == 1)
//		{
//			count++;
//		}
//	}
//	printf("%d", count);
//	return 0;
//}

//解法3:
//int main()
//{
//	int n = 0;
//	scanf("%d", &n);
//	int count = 0;
//	while (n)
//	{
//		n = n & (n - 1);//这个表达式能将n的二进制中最右边的1去掉
//		count++;
//	}
//	printf("%d", count);
//	return 0;
//}

//n&(n-1)的练习:判断一个数是否是2的次方
// 解法1:
//int main()
//{
//	int n = 0;
//	scanf("%d", &n);
//	int count = 0;
//	while (n)
//	{
//		n = n & (n - 1);
//		count++;
//	}
//	if (count == 1)
//	{
//		printf("yes\n");
//	}
//	else
//	{
//		printf("no\n");
//	}
//	return 0;
//}

//解法2:
//int main()
//{
//	int n = 0;
//	scanf("%d", &n);
//	if ((n & (n - 1)) == 0)
//	{
//		printf("yes\n");
//	}
//	else
//	{
//		printf("no\n");
//	}
//	return 0;
//}

//练习2:
//将13二进制序列的第5位修改为1，然后再改为0
//int main()
//{
//	//13的二进制序列:00000000 00000000 00000000 00001101
//	//第五位修改为1:第五位 按位或1，其他位 按位或0
//	int a = 13;
//	a |= (1 << 4);//得到:00000000 00000000 00000000 00011101
//	printf("a=%d\n", a);//a=29
//	//再修改为0:第五位按位与0 其他位置可以按位与1 或者 按位或0（最好是前后的位操作符相同）
//	a &= (~(1 << 4));//先把1向左移动到第五位，再取反
//	//相当于:00000000 00000000 00000000 00011101 (a)
//	//按位与:11111111 11111111 11111111 11101111 (~(1<<4))
//	printf("a=%d\n", a);
//	return 0;
//}

//7. 逗号表达式
//exp1, exp2, exp3, …expN
//逗号表达式，就是⽤逗号隔开的多个表达式。
//逗号表达式，从左向右依次执⾏。整个表达式的结果是最后⼀个表达式的结果。
//int main()
//{
//	int a = 1;
//	int b = 2;
//	int c = (a > b, a = b + 10, a, b = a + 1);//从左向右，结果是最后的表达式
//	return 0;
//}

//8. 下标访问[]、函数调⽤
//8.1[] 下标引⽤操作符
//操作数：⼀个数组名 + ⼀个索引值(下标)

//8.2 函数调⽤操作符（）
//接受⼀个或者多个操作数：第⼀个操作数是函数名，剩余的操作数就是传递给函数的参数。
